`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    17:53:07 11/05/2011 
// Design Name: 
// Module Name:    axis_up_dmux 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module axis_pcie_dmux #(
  parameter   UP_STREAM_DMUX = 0)
(
  input               clk,
  input               rst,
  
  input               s_tvalid,
  output              s_tready,
  input   [127:0]     s_tdata,
  input   [3:0]       s_tstrb,
  input               s_tlast,

  output              m0_tvalid,
  input               m0_tready,
  output              m1_tvalid,
  input               m1_tready,

  input   [7:0]       bus,

  input   [31:12]     io_min_0,
  input   [31:12]     io_max_0,
  input   [31:20]     mem_min_0,
  input   [31:20]     mem_max_0,
  input   [63:20]     pmem_min_0,
  input   [63:20]     pmem_max_0,
  input   [7:0]       bus_min_0,
  input   [7:0]       bus_max_0,
  input   [4:0]       device_0,

  input   [31:12]     io_min_1,
  input   [31:12]     io_max_1,
  input   [31:20]     mem_min_1,
  input   [31:20]     mem_max_1,
  input   [63:20]     pmem_min_1,
  input   [63:20]     pmem_max_1,
  input   [7:0]       bus_min_1,
  input   [7:0]       bus_max_1,
  input   [4:0]       device_1
  );

  localparam
    MRd     = 8'b00x00000,
    MRdLk   = 8'b00x00001,
    MWr     = 8'b01x00000,
    IORd    = 8'b00000010,
    IOWr    = 8'b01000010,
    CfgRd0  = 8'b00000100,
    CfgWr0  = 8'b01000100,
    CfgRd1  = 8'b00000101,
    CfgWr1  = 8'b01000101,
    TCfgRd  = 8'b00011011,
    TCfgWr  = 8'b01011011,
    Msg     = 8'b00110xxx,
    MsgD    = 8'b01110xxx,
    Cpl     = 8'b00001010,
    CplD    = 8'b01001010,
    CplLk   = 8'b00001011,
    CplLkD  = 8'b01001011;
    
  localparam
    CHECK_HEADER = 0,
    PASS_0 = 1,
    PASS_1 = 2,
    DROP = 3;
  
  reg [2:0] rState, sState;

  wire [4:0]  tlp_type      = s_tdata[28:24];
  wire [2:0]  tlp_fmt       = s_tdata[31:29];
  wire [7:0]  tlp_msgcode   = s_tdata[39:32];
  wire [2:0]  tlp_function  = s_tdata[82:80];
  wire [4:0]  tlp_device    = s_tdata[87:83];
  wire [7:0]  tlp_bus       = s_tdata[95:88];
  wire [63:0] tlp_addr      = tlp_fmt[0] ? {s_tdata[95:64], s_tdata[127:98], 2'b0} : {32'b0, s_tdata[95:66], 2'b0};
  wire [7:0] tlp_fmt_type   = {tlp_fmt, tlp_type};
  
  reg address_routing;
  reg id_routing;
  reg implicit_routing;
  
  reg address_match_0;
  reg address_match_1;
  reg id_match_0;
  reg id_match_1;
  
  reg bus_match;
  
  always @*
  begin
    address_routing <= 1'b0;
    id_routing <= 1'b0;
    implicit_routing <= 1'b0;
    casex(tlp_fmt_type)
      MRd     : address_routing <= 1'b1;
      MRdLk   : address_routing <= 1'b1;
      MWr     : address_routing <= 1'b1;
      IORd    : address_routing <= 1'b1;
      IOWr    : address_routing <= 1'b1;
      CfgRd0  : id_routing <= 1'b1;
      CfgWr0  : id_routing <= 1'b1;
      CfgRd1  : id_routing <= 1'b1;
      CfgWr1  : id_routing <= 1'b1;
      TCfgRd  : id_routing <= 1'b1;
      TCfgWr  : id_routing <= 1'b1;
      Msg, MsgD:
        case(tlp_type[2:0])
          3'b000: implicit_routing <= 1'b1;
          3'b001: address_routing <= 1'b1;
          3'b010: id_routing <= 1'b1;
          3'b011: implicit_routing <= 1'b1;
          3'b100: implicit_routing <= 1'b1;
          3'b101: implicit_routing <= 1'b1;
        endcase
      Cpl     : id_routing <= 1'b1;
      CplD    : id_routing <= 1'b1;
      CplLk   : id_routing <= 1'b1;
      CplLkD  : id_routing <= 1'b1;
    endcase
  end
  
  always @* bus_match <= (bus == tlp_bus);
  
  reg addr32_match_0;
  reg addr64_match_0;
  reg bus_match_0;
  reg device_match_0;
  
  always @*
  begin
    device_match_0 <= (device_0 == tlp_device);
    bus_match_0    <= (bus_min_0 <= tlp_bus)          && (tlp_bus <= bus_max_0);
    addr32_match_0 <= (mem_min_0 <= tlp_addr[31:20])  && (tlp_addr[31:20] <= mem_max_0) && !(|tlp_addr[63:32]);
    addr64_match_0 <= (pmem_min_0 <= tlp_addr[63:20]) && (tlp_addr[63:20] <= pmem_max_0) && (|tlp_addr[63:32]);
    casex(tlp_fmt_type)
      MRd, MRdLk, MWr, IORd, IOWr: 
        address_match_0 <= addr32_match_0 || addr64_match_0;
      CfgRd0, CfgWr0:
        id_match_0 <= device_match_0;
      CfgRd1, CfgWr1:
        id_match_0 <= bus_match_0;
      TCfgRd, TCfgWr, Cpl, CplD, CplLk, CplLkD:
//        id_match_0 <= bus_match_0 || (bus_match && device_match_0);
        id_match_0 <= bus_match_0;// || (bus_match && device_match_0);
      Msg, MsgD: if (tlp_type[2:0])
        id_match_0 <= bus_match_0;
    endcase
  end
  
  reg addr32_match_1;
  reg addr64_match_1;
  reg bus_match_1;
  reg device_match_1;

  always @*
  begin
    device_match_1 <= (device_1 == tlp_device);
    bus_match_1    <= (bus_min_1 <= tlp_bus)          && (tlp_bus <= bus_max_1);
    addr32_match_1 <= (mem_min_1 <= tlp_addr[31:20])  && (tlp_addr[31:20] <= mem_max_1) && !(|tlp_addr[63:32]);
    addr64_match_1 <= (pmem_min_1 <= tlp_addr[63:20]) && (tlp_addr[63:20] <= pmem_max_1) && (|tlp_addr[63:32]);
    casex(tlp_fmt_type)
      MRd, MRdLk, MWr, IORd, IOWr: 
        address_match_1 <= addr32_match_1 || addr64_match_1;
      CfgRd0, CfgWr0:
        id_match_1 <= device_match_1; 
      CfgRd1, CfgWr1:
        id_match_1 <= bus_match_1;
      TCfgRd, TCfgWr, Cpl, CplD, CplLk, CplLkD:
//        id_match_1 <= bus_match_1 && device_match_1; 
//        id_match_1 <= bus_match_1 || (bus_match && device_match_1); 
        id_match_1 <= bus_match_1;// || (bus_match && device_match_1); 
      Msg, MsgD: if (tlp_type[2:0])
        id_match_1 <= bus_match_1;
    endcase
  end
  
  always @(posedge clk)
  begin
    if (rst)
      rState <= CHECK_HEADER;
    else
      rState <= sState;
  end
  
  always @*
  begin
    sState <= rState;
    case (rState)
      CHECK_HEADER:
        if (s_tvalid)
        begin
          if (address_routing)
          begin
            if (address_match_0)
              sState <= PASS_0;
            else if (address_match_1)
              sState <= PASS_1;
            else
              sState <= DROP;
          end
          else if (id_routing)
          begin
            if (id_match_0)
              sState <= PASS_0;
            else if (id_match_1)
              sState <= PASS_1;
            else
              sState <= DROP;
          end
          else
            sState <= DROP;
        end
      PASS_0:
        if (m0_tvalid && m0_tready && s_tlast)
          sState <= CHECK_HEADER;
      PASS_1:
        if (m1_tvalid && m1_tready && s_tlast)
          sState <= CHECK_HEADER;
      DROP:
        if (s_tvalid && s_tlast)
          sState <= CHECK_HEADER;
    endcase
  end

  assign s_tready = (rState == PASS_0) && m0_tready || 
                    (rState == PASS_1) && m1_tready || 
                    (rState == DROP);
  assign m0_tvalid = (rState == PASS_0) && s_tvalid;
  assign m1_tvalid = (rState == PASS_1) && s_tvalid;
endmodule
